Временной ряд

Данные – траты жителей Великобритании за рубежом с января 1980г. по декабрь 2016г. в миллионах £.

Чуть более свежие данные можно найти здесь: https://www.ons.gov.uk/peoplepopulationandcommunity/leisureandtourism/timeseries/gmam/ott

uk <- read.csv("UK.csv", sep = ',', stringsAsFactors = FALSE)
uk <- ts(uk$EXPEND, start = c(1980, 1), frequency = 12)
df.uk <- data.frame(seq.Date(from = as.Date("1980-01-01"), to = as.Date("2016-12-01"), by = "month"), uk)
colnames(df.uk) <- c("DATE", "EXP")
tail(uk, 24)
      Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec
2015 2371 2071 2548 2901 3255 3690 4442 5506 4461 3445 2365 1972
2016 2448 2523 2607 3529 3489 3969 4569 6097 5215 4030 2820 2120
plot(uk)

Тренд сначала похож на линейный, примерно в 2008 году резко меняется. Имеет смысл рассматривать только конец ряда, сославшись на то, что резкое изменение тренда связано с финансовым кризисом.

Модель не аддитивная, амплитуда сезонной компоненты не постоянна. Посмотрим, что происходит при логарифмировании.

plot(log(uk))

Амплитуда не постоянна, значит, модель не является мультипликативной.

Периодограмма

\((x_1, \ldots, x_N)\) – ряд.

Представим его в виде \(x_n = C_0 + \sum\limits_{k=1}^{[(N-1)/2]}(C_kcos(2\pi n k/N) + S_ksin(2\pi n k/N))\) (\(+ C_{N/2}(-1)^n\)), если \(N\) – четное.

Общий вид периодограммы:

\(\widetilde{П}_x^N(w) = \frac{1}{N}|\sum\limits_{n = 0}^{N - 1} e^{-2\pi iwn} x_{n + 1}|^2\), \(w \in (-1/2, 1/2)\)

Так как ряд вещественный, рассматриваем \(П_x^N(k/N) = \frac{N}{2}\begin{cases} 2C_0^2, &k = 0\\ C_k^2 + S_k^2, &0 < k < \frac{N}{2}\\ 2C_{N/2}^2, &k = \frac{N}{2} \end{cases}\)

spec.pgram(uk, detrend = FALSE, fast = FALSE, log='no', xaxt = 'n')
axis(1, at = c(0,1,2,3,4,5,6), labels = c('0', '1/12', '2/12', '3/12', '4/12', '5/12', '6/12'))

spec.pgram(uk, detrend = TRUE, fast = FALSE, log='no', xaxt = 'n')
axis(1, at = c(0,1,2,3,4,5,6), labels = c('0', '1/12', '2/12', '3/12', '4/12', '5/12', '6/12'))

Заметны периоды 12 (год), 6 (полгода), 4 (квартал), 2.4, 2 и частота близкая к нулю.

Отступление: Пример про растекание частоты

par(mfrow=c(1,2))
spec.pgram(cos(2*pi*1:92/10), detrend = FALSE, fast = FALSE, log='yes', taper=0, pad=0) #растекается
spec.pgram(cos(2*pi*1:100/10), detrend = FALSE, fast = FALSE, log='yes', taper=0, pad = 0)#не растекается

Отступление: белый шум

  • \(w_t, t = 0, \pm 1, \pm 2, \ldots\) – некоррелированые случайные величины, \(\mathbb{E}w_t = 0,~\sigma_w^2\), пишут, например, \(w_t \sim wn(0,~\sigma_w^2)\).

  • если кроме того \(w_t\) – iid, пишут \(iid(0,~\sigma_w^2)\)

  • если \(w_t\) – iid и \(w_t \sim N(0, ~\sigma_w^2)\), шум гауссовский

Периодограмма гауссовского шума

wn <- ts(rnorm(1000))
spec.pgram(wn, detrend = FALSE, log = 'no')

spec.pgram(wn, detrend = FALSE, log = 'yes')

Автоковариационная функция

acf(wn)

Cглаживание временного ряда и выделение тренда

Тренд – полиномиальная аппроксимация некоторой параметрической функции.

Тренд – медленно меняющаяся компонента ряда.

Тренд – случайный процесс.

Чем отличается сглаживание от выделения тренда?

  • Сглаживание даст медленно меняющуюся компоненту. Если понимать тренд как некоторую неслучайную функцию, то можем получить тренд, а можем и нет.

Линейный фильтр, FIR, причинный фильтр, АЧХ, ФЧХ

\(x_n\) – ряд.

Фильтры

  • \(y_j = (\Phi(X))_j = \sum\limits_{i = -\infty}^{\infty} h_i x_{j- i}\) – линейный фильтр

  • \(\{h_i\}\) – импульсная характеристика (impulse response)

  • FIR – finite impulse response, конечное число \(h_i\) ненулевые, т.е. \((\Phi(X))_j = \sum\limits_{i = -r_1}^{r_2} h_i x_{j- i}\)

  • Причинный фильтр (casual filter), смотрим только в “прошлое”: \((\Phi(X))_j = \sum\limits_{i = 0}^{r} h_i x_{j- i}\)

АЧХ, ФЧХ

  • \(H_{\Phi}(z) = \sum\limits_i h_i z^{-i}\) – передаточная функция (transfer function)

  • \(|H_{\Phi}(e^{i2\pi w})|\) – Амплитудно частотная характеристика (АЧХ, frequency response), показывает как меняется амплитуда в зависимости от частоты

  • \(\varphi_\Phi(w) = Arg H_{\Phi}(\varphi^{i2\pi w})\) – Фазочастотная характеристика (phase response)

Cкользящее среднее

Скользящее среднее – линейный фильтр, для которого \(h_i = \frac{1}{2M + 1}\).

MovAvg <- function(series, M){
  
  avg <- sapply(1:length(series), FUN = function(i){
    l <- ((i-M):(i-1))
    l <- l[l > 0]
    
    r <- ((i + 1):(i+M))
    r <- r[r <= length(series)]
    
    h <- 1/(length(r) + length(l) + 1)
    
    sum(series[c(l,i,r)] * h) 
  })
}
frequencyResponse <- function(h, w){
  fr <- w
  if(max(fr) > 0.5) fr <- fr / (max(w) * 2)
  sapply(fr, function(freq){
    cs <- cos(2*pi*freq *(1:length(h)))
    pr <- h * cs
    return(abs(sum(pr)))
  })
}
periodogramBuilder <- function(series, filtered){
   par(mfrow=c(1,3))
   
   sp.initial <- spec.pgram(series, detrend = FALSE, fast = FALSE, log='no', taper=0)
   sp.filtered <- spec.pgram(na.omit(filtered), detrend = FALSE, fast = FALSE, log='no', taper=0)
   sp.difference <- spec.pgram(na.omit(series - filtered), detrend = FALSE, fast = FALSE, log='no', taper=0)
   par(mfrow=c(1,1))
}
movingAverageFiltering <- function(timeSeries,m){
  if( !is.ts(timeSeries)){
    stop("Object given is not a ts object!")
  }
  
  par(mfrow=c(1,1))
  #Initial
  plot(timeSeries, type='l', lty=2, ylab = 'Rate')
  
  h <- rep(1,(2*m+1))/(2*m+1)
  #average
  filtered <- ts(MovAvg(timeSeries, m), start = c(1980, 1), frequency = 12) 
  lines(filtered, col='red')
  #difference (detrended)
  difference <- timeSeries - filtered
  plot(difference, type = 'l')
  
  #comparing periodograms
  periodogramBuilder(timeSeries, filtered)
  #Frequency response
  par(mfrow=c(1,1))
  sp.filtered <- spec.pgram(na.omit(filtered), detrend = FALSE, fast = FALSE, log='no', taper=0, plot = FALSE)
  fr.filtered <- frequencyResponse(h, sp.filtered$freq)
  plot(x = sp.filtered$freq, y = fr.filtered, type = 'l', xlab = "frequency", ylab = "response")
}

Сгладим ряд. Выберем окно кратное 12, чтобы убрать сезонность.

movingAverageFiltering(uk, 12)

Вся периодика ушла в остаток. Компонента, соответствующая низкой частоте, смешалась с трендом.

Увиличиваем окно

movingAverageFiltering(uk, 24)

movingAverageFiltering(uk, 36)

movingAverageFiltering(uk, 60)

Чем больше окно, тем сильнее подавляем высокие частоты, что видно по АЧХ.

“Low-pass filter” – высокие частоты убираем, низкие оставляем.

Смещение при сглаживании фильтром скользящего среднего. Роль второй производной

Общий вид фильтра: \[\begin{align*} y(a) = \int_{-\delta}^{\delta} f(a + x) w(x) dx, \quad \int_{-\delta}^{\delta} w(x)dx = 1,\quad \int_{-\delta}^{\delta} x w(x)dx = 0. \end{align*}\]

Пусть \(f\) – некоторая гладкая функция, применим к ней фильтр.

\[\begin{align*} f(a + x) = f(a) + f'(a)x + f''(a)\frac{x^2}{2} + \ldots,\\ y(a) \approx f(a) + f'(a) 0 + f''(a)\frac{x^2}{2} \int_{-\delta}^{\delta} x^2 w(x)dx. \end{align*}\]

В случае скользящего среднего \(w(x) = \frac{1}{2\delta}\), следовательно, у нас всегда будет смещение \(\approx \frac{\delta^2}{3} \frac{f''(a)}{2}\).

Отступление: перерисовка и запаздывание

Перерисовка

Возникает из-за того, что нам нехватает точек справа, чтобы посчитать среднее. При добавлении новой точки среднее пересчитывается.

Последовательно добавляем последние 100 точек.

plot(y = uk, x = (1:444), type = 'l')
for(i in 100:1){
  ma <- MovAvg(head(uk, -i), 12)
  lines(ma, type = 'l', col = 'red', ylim = c(0, 6000), xlim = c(0, 450))
}

Запаздывание

Рассмотрим причинный фильтр, например скользящее среднее, которое считается только по “прошлому”.

MovAvg.cause <- function(series, M){
  avg <- sapply(1:length(series), FUN = function(i){
    l <- ((i- M + 1):(i-1))
    l <- l[l > 0]
    h <- 1/length(l)
    sum(series[c(l,i)] * h) 
  })
}
plot(y = uk, x = (1:444), type = 'l')
ma.c <- MovAvg.cause(uk, 24)
ma <- MovAvg(uk, 24)
lines(ma.c, type = 'l', col = 'red', ylim = c(0, 6000), xlim = c(0, 450))
lines(ma, type = 'l', col = 'blue', ylim = c(0, 6000), xlim = c(0, 450))
legend("topleft", c("Usual", "Casual"), lty=c(1,1), lwd=c(2.5,2.5),col=c("blue","red"))

Видно, что casual фильтр “догоняет” обычное среднее только через некоторое количество точек.

Скользящая медиана

В отличие от скользяещего среднего, устойчива к аутлаерам. Но негладкая и не является оценкой м.о. для несимметричного ряда. Иногда сначала применяют скользящую медиану, а потом результат сглаживают скользящим средним.

movingMedianFiltering <- function(timeSeries,k){
  if( !is.ts(timeSeries)){
    stop("Object given is not a ts object!")
  }
  
  par(mfrow=c(1,1))
  #Initial
  plot(timeSeries, type='l', lty=2, ylab = 'Rate')
  #median
  filtered <- runmed(timeSeries, k)
  lines(ts(filtered, start = c(1980, 1), frequency = 12 ) , col='red', type='l')
  #difference (detrended)
  difference <- timeSeries - filtered
  plot(difference, type = 'l')
  
  #comparing periodograms
  
  periodogramBuilder(timeSeries, filtered)
}
movingMedianFiltering(uk, 13)

Разность первого порядка

df <- diff(uk)
h.df <- c(-1,1)
plot(df, type='l', main='difference')
par(mfrow=c(1,2))

sp.initial <- spec.pgram(uk, detrend = FALSE, fast = FALSE, log='no', taper=0)
sp.df <- spec.pgram(na.omit(df), detrend = FALSE, fast = FALSE, log='no', taper=0)
par(mfrow=c(1,1))

fr.df <- frequencyResponse(h.df, sp.df$freq/12)
plot(x = sp.df$freq, y = fr.df, type = 'l', xlab = 'frequency', ylab = 'response')

“High-pass filter” – пропускает высокие частоты, низкие убирает.

Почему переход к разностям это хорошо?

  • Если модель нашего ряда имеет вид \(x_t = \mu_t + y_t\), где \(\mu_t\) – тренд, \(y_t\) – стационарный процесс, то при переходе к разностям получим стационарный ряд, особенно, если тренд фиксирован (например, линейный)

  • Уберем низкие частоты, усилим вклад высоких

  • Уберем линейный тренд (если перейдем к разностям второго порядка, то квадратичный и т.д.)

Минусы

  • Увеличиваем вклад шума

Сглаживание и выделение тренда с помощью регрессии

Линейная регрессия

fit <- lm(df.uk$EXP ~ df.uk$DATE, unemployment.data)
plot(df.uk, type='l')
abline(fit, col='red')

periodogramBuilder(df.uk$EXP, fit$fitted.values)

plot(fit$residuals, type='l')

Полиномиальная регрессия

cs <- cos(2*pi*(1:length(uk))/12)
sn <- sin(2*pi*(1:length(uk))/12)
fit <- lm(EXP ~ stats::poly(df.uk$DATE, 10)
                  , df.uk)
plot(df.uk, type='l', ylab="EXP")
lines(y= fitted(fit), x= df.uk$DATE, col='red')

plot(fit$residuals, type='l')

periodogramBuilder(df.uk$EXP, fit$fitted.values)

Kernel Smoothing (kernel regression)

Kernel Smoothing – скользящее среднее, в котором используется весовая функция (kernel).

Cчитаем, что наш ряд имеет вид: \[\begin{align*} x_t = f_t + y_t, \end{align*}\] где \(f_t\) – некоторая гладкая функция, \(y_t\) – стационарный процесс. Тогда \[\begin{align*} \hat{f}_t = \sum_{i = 1}^{n}w_i(t)x_i, \end{align*}\] где \[\begin{align*} w_i(t) = K(\frac{t-i}{b}) / \sum_{j = 1}^{n}K(\frac{t-j}{b}). \end{align*}\]

Обычно \(K(z) = \frac{1}{\sqrt{2\pi}} \exp(-z^2 / 2)\).

plot(uk, type='l', ylab="EXP")
trend.sm <- ksmooth(time(uk), uk, 'normal', bandwidth = 2)
lines(trend.sm, col='red')

plot(uk - trend.sm$y)

periodogramBuilder(uk, trend.sm$y)

Nearest Neighbor Regression

Строим линейную регрессию на \(k\) ближайших соседей. Т.е. предсказываем \(x_t\) по \(\{x_{t-k/2},\ldots, x_t,\ldots, x_{t+k/2}\}\).

plot(uk, type='l', ylab="m £")
neigh.t <- supsmu(time(uk), uk, span = .5)
neigh.s <- supsmu(time(uk), uk, span = .01)
lines(neigh.t, col='red')
lines(neigh.s, col='blue')

plot(uk - neigh.t$y)

periodogramBuilder(uk, neigh.t$y)

plot(uk - neigh.s$y)

periodogramBuilder(uk, neigh.s$y)

Seasonal and Trend Decomposition (STL)

Хотим представить ряд в виде \(X_n = T_n + S_n + N_n\).

Будем делать все для логарифмированного ряда, так как он похож на линейную модель (амплитуда примерно одинакова). Можно заменить далее сложение на умножение, разность на деление и получить алгоритм для мультипликативной модели.

Хорошее описание алгоритма нашел здесь: http://www.abs.gov.au/websitedbs/d3310114.nsf/51c9a3d36edfd0dfca256acb00118404/c890aa8e65957397ca256ce10018c9d8!opendocument

  • Начальная оценка тренда. Применяем скользящее среднее с длиной окна T, в нашем случе кратной 12.
T1 <- MovAvg(log(uk), 24)
SN1 <- log(uk) - T1
periodogramBuilder(log(uk), T1)

  • Получили ряд в виде \(X_n = \widetilde{T}_n + \widetilde{S_n + N_n}\)

  • Оценка сезонной компоненты. Сглаживаем \(\widetilde{S_n + N_n}\) с маленьким окном. Получаем \(\widetilde{S_n + N_n} = \widetilde{S}_n + \widetilde{N}_n\).

  • Убираем полученную сезонность из исходного ряда, получаем оценку исправленного ряда (adjusted).

S1 <- MovAvg(SN1, 2)
ADJ1 <- log(uk) - S1
periodogramBuilder(SN1, S1)

  • Улучшенная оценка тренда. Применяем скользящее среднее с большим окном к исправленному ряду \(X_n - \widetilde{S}_n\), получаем \(\widetilde{\widetilde{T}}_n + \widetilde{\widetilde{N}}_n\)

  • Отсюда второй раз оцениваем \(\widetilde{\widetilde{S_n + N_n}} = X_n - \widetilde{\widetilde{T}}_n\)

T2 <- MovAvg(ADJ1, 24)
SN2 <- log(uk) - T2
periodogramBuilder(ADJ1, T2)

  • Применяем скользящее среднее с маленьким окном к \(\widetilde{\widetilde{S_n + N_n}}\), получаем \(\widetilde{\widetilde{S}}_n + \widetilde{\widetilde{\widetilde{N}}}_n\). Cнова оцениваем исправленный ряд
S2 <- MovAvg(SN2, 2)
ADJ2 <- log(uk) - S2
periodogramBuilder(SN2, S2)

  • Финальная оценка тренда и шума
T3 <- MovAvg(ADJ2, 24)
N1 <- ADJ2 - T3
periodogramBuilder(ADJ2, T3)

  • Получили \(X_n = \widetilde{\widetilde{T}}_n + \widetilde{\widetilde{S}}_n + \widetilde{\widetilde{\widetilde{N}}}_n\)
par(mfrow=c(4,1))
plot(log(uk), type = 'l', ylab='Original')
plot(T3, type='l', ylab='Trend')
plot(S2, type='l', ylab = 'Seasonal Component')
plot(N1, type='l', ylab='Noise')

acf(N1, lag.max = 100)

Шум белым не получился. Как видно из последних периодограмм, много периодических компонент так и не выделились в сезонную компоненту.

STL (LOESS)

uk.stl <- stl(uk, s.window = 13, l.window = 13, outer = 0, inner = 2, t.window = 35)
plot(uk.stl)

Стабилизация дисперсии

Остаток после stl

rem <- uk.stl$time.series[,3]
plot(rem)

Прологорифмируем исходный ряд и снова посмотрим на остаток после выделения тренда.

uk.log <- log(uk)
uk.log.stl <- stl(uk.log, s.window = 13, l.window = 13, outer = 0, inner = 2, t.window = 35)
log.rem <- uk.log.stl$time.series[,3]
plot(log.rem)

Оценим дисперсию

Идея * Выделив тренд, получили шум с некоторой дисперсией \(\xi_n = \sigma(n)\eps_n\)

  • Выделить тренд у \(\xi_n^2 = \sigma(n)^2\eps_n^2\) – все равно, что найти среднее \(\mathbf{E}\xi_n^2 = \sigma(n)^2\)

Возведем остаток в квадрат и выделим тренд скользящим средним

dif.sqr <- ts(log.rem^2)
plot(dif.sqr, type = 'l')
sigma.n.sqr <- MovAvg(dif.sqr, 24)
lines(sigma.n.sqr, col='red')
legend("topleft", c("Rem^2", "sigma(n)^2"), lty=c(1,1), lwd=c(2.5,2.5),col=c("black","red"))

Построим огибающую

noise <- ts(log.rem / sqrt(sigma.n.sqr))
plot(log.rem, type = 'l')
lines(ts(sqrt(sigma.n.sqr), start = c(1980, 1), frequency = 12), col = 'blue')
lines(ts(-sqrt(sigma.n.sqr), start = c(1980, 1), frequency = 12), col = 'blue')

Оценка шума, ее периодограмма и автоковариационная функция

plot(noise)

spec.pgram(na.omit(noise), detrend = FALSE, fast = FALSE, log='no', taper=0)

acf(noise, lag.max = 50)

LS0tCnRpdGxlOiAi0JDQvdCw0LvQuNC3INCy0YDQtdC80LXQvdC90L7Qs9C+INGA0Y/QtNCwIgphdXRob3I6ICLQktC70LDQtNC40LzQuNGAINCQ0LPQtdC10LIiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgICAgdG9jOiB5ZXMKICAgICAgdG9jX2Zsb2F0OiB5ZXMKICAgICAgdG9jX2RlcHRoOiAzCi0tLQpgYGB7ciwgZWNobz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShkcGx5cikKbGlicmFyeShmb3JlY2FzdCkKbGlicmFyeShSc3NhKQpgYGAKCiPQktGA0LXQvNC10L3QvdC+0Lkg0YDRj9C0CgrQlNCw0L3QvdGL0LUgLS0g0YLRgNCw0YLRiyDQttC40YLQtdC70LXQuSDQktC10LvQuNC60L7QsdGA0LjRgtCw0L3QuNC4INC30LAg0YDRg9Cx0LXQttC+0Lwg0YEg0Y/QvdCy0LDRgNGPIDE5ODDQsy4g0L/QviDQtNC10LrQsNCx0YDRjCAyMDE20LMuINCyINC80LjQu9C70LjQvtC90LDRhSDCoy4KCtCn0YPRgtGMINCx0L7Qu9C10LUg0YHQstC10LbQuNC1INC00LDQvdC90YvQtSDQvNC+0LbQvdC+INC90LDQudGC0Lgg0LfQtNC10YHRjDogaHR0cHM6Ly93d3cub25zLmdvdi51ay9wZW9wbGVwb3B1bGF0aW9uYW5kY29tbXVuaXR5L2xlaXN1cmVhbmR0b3VyaXNtL3RpbWVzZXJpZXMvZ21hbS9vdHQKYGBge3J9CnVrIDwtIHJlYWQuY3N2KCJVSy5jc3YiLCBzZXAgPSAnLCcsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKdWsgPC0gdHModWskRVhQRU5ELCBzdGFydCA9IGMoMTk4MCwgMSksIGZyZXF1ZW5jeSA9IDEyKQoKZGYudWsgPC0gZGF0YS5mcmFtZShzZXEuRGF0ZShmcm9tID0gYXMuRGF0ZSgiMTk4MC0wMS0wMSIpLCB0byA9IGFzLkRhdGUoIjIwMTYtMTItMDEiKSwgYnkgPSAibW9udGgiKSwgdWspCmNvbG5hbWVzKGRmLnVrKSA8LSBjKCJEQVRFIiwgIkVYUCIpCnRhaWwodWssIDI0KQpgYGAKYGBge3IsIGZpZy53aWR0aD0gMTAsIGZpZy5oZWlnaHQ9IDV9CnBsb3QodWspCmBgYArQotGA0LXQvdC0INGB0L3QsNGH0LDQu9CwINC/0L7RhdC+0LYg0L3QsCDQu9C40L3QtdC50L3Ri9C5LCDQv9GA0LjQvNC10YDQvdC+INCyIDIwMDgg0LPQvtC00YMg0YDQtdC30LrQviDQvNC10L3Rj9C10YLRgdGPLiDQmNC80LXQtdGCINGB0LzRi9GB0Lsg0YDQsNGB0YHQvNCw0YLRgNC40LLQsNGC0Ywg0YLQvtC70YzQutC+INC60L7QvdC10YYg0YDRj9C00LAsINGB0L7RgdC70LDQstGI0LjRgdGMINC90LAg0YLQviwg0YfRgtC+INGA0LXQt9C60L7QtSDQuNC30LzQtdC90LXQvdC40LUg0YLRgNC10L3QtNCwINGB0LLRj9C30LDQvdC+INGBINGE0LjQvdCw0L3RgdC+0LLRi9C8INC60YDQuNC30LjRgdC+0LwuCgrQnNC+0LTQtdC70Ywg0L3QtSDQsNC00LTQuNGC0LjQstC90LDRjywg0LDQvNC/0LvQuNGC0YPQtNCwINGB0LXQt9C+0L3QvdC+0Lkg0LrQvtC80L/QvtC90LXQvdGC0Ysg0L3QtSDQv9C+0YHRgtC+0Y/QvdC90LAuINCf0L7RgdC80L7RgtGA0LjQvCwg0YfRgtC+INC/0YDQvtC40YHRhdC+0LTQuNGCINC/0YDQuCDQu9C+0LPQsNGA0LjRhNC80LjRgNC+0LLQsNC90LjQuC4KCmBgYHtyLCBmaWcud2lkdGg9IDEwLCBmaWcuaGVpZ2h0PSA1fQpwbG90KGxvZyh1aykpCmBgYAoK0JDQvNC/0LvQuNGC0YPQtNCwINC90LUg0L/QvtGB0YLQvtGP0L3QvdCwLCDQt9C90LDRh9C40YIsINC80L7QtNC10LvRjCDQvdC1INGP0LLQu9GP0LXRgtGB0Y8g0LzRg9C70YzRgtC40L/Qu9C40LrQsNGC0LjQstC90L7QuS4gCgoj0J/QtdGA0LjQvtC00L7Qs9GA0LDQvNC80LAKJCh4XzEsIFxsZG90cywgeF9OKSQgLS0g0YDRj9C0LiAKCtCf0YDQtdC00YHRgtCw0LLQuNC8INC10LPQviDQsiDQstC40LTQtQokeF9uID0gQ18wICsgXHN1bVxsaW1pdHNfe2s9MX1ee1soTi0xKS8yXX0oQ19rY29zKDJccGkgbiBrL04pICsgU19rc2luKDJccGkgbiBrL04pKSQgICgkKyBDX3tOLzJ9KC0xKV5uJCksINC10YHQu9C4ICROJCAtLSDRh9C10YLQvdC+0LUuCgrQntCx0YnQuNC5INCy0LjQtCDQv9C10YDQuNC+0LTQvtCz0YDQsNC80LzRizoKCiRcd2lkZXRpbGRle9CffV94Xk4odykgPSBcZnJhY3sxfXtOfXxcc3VtXGxpbWl0c197biA9IDB9XntOIC0gMX0gZV57LTJccGkgaXdufSB4X3tuICsgMX18XjIkLCAkdyBcaW4gKC0xLzIsIDEvMikkCgrQotCw0Log0LrQsNC6INGA0Y/QtCDQstC10YnQtdGB0YLQstC10L3QvdGL0LksICDRgNCw0YHRgdC80LDRgtGA0LjQstCw0LXQvAok0J9feF5OKGsvTikgPSBcZnJhY3tOfXsyfVxiZWdpbntjYXNlc30KMkNfMF4yLCAmayA9IDBcXApDX2teMiArIFNfa14yLCAmMCA8IGsgPCBcZnJhY3tOfXsyfVxcCjJDX3tOLzJ9XjIsICZrID0gXGZyYWN7Tn17Mn0KXGVuZHtjYXNlc30kCgoKCmBgYHtyLCBmaWcud2lkdGg9IDEwLCBmaWcuaGVpZ2h0PSA1fQpzcGVjLnBncmFtKHVrLCBkZXRyZW5kID0gRkFMU0UsIGZhc3QgPSBGQUxTRSwgbG9nPSdubycsIHhheHQgPSAnbicpCmF4aXMoMSwgYXQgPSBjKDAsMSwyLDMsNCw1LDYpLCBsYWJlbHMgPSBjKCcwJywgJzEvMTInLCAnMi8xMicsICczLzEyJywgJzQvMTInLCAnNS8xMicsICc2LzEyJykpCmBgYApgYGB7ciwgZmlnLndpZHRoPSAxMCwgZmlnLmhlaWdodD0gNX0Kc3BlYy5wZ3JhbSh1aywgZGV0cmVuZCA9IFRSVUUsIGZhc3QgPSBGQUxTRSwgbG9nPSdubycsIHhheHQgPSAnbicpCmF4aXMoMSwgYXQgPSBjKDAsMSwyLDMsNCw1LDYpLCBsYWJlbHMgPSBjKCcwJywgJzEvMTInLCAnMi8xMicsICczLzEyJywgJzQvMTInLCAnNS8xMicsICc2LzEyJykpCmBgYArQl9Cw0LzQtdGC0L3RiyDQv9C10YDQuNC+0LTRiyAxMiAo0LPQvtC0KSwgNiAo0L/QvtC70LPQvtC00LApLCA0ICjQutCy0LDRgNGC0LDQuyksIDIuNCwgMiDQuCDRh9Cw0YHRgtC+0YLQsCDQsdC70LjQt9C60LDRjyDQuiDQvdGD0LvRji4KCgojI9Ce0YLRgdGC0YPQv9C70LXQvdC40LU6INCf0YDQuNC80LXRgCDQv9GA0L4g0YDQsNGB0YLQtdC60LDQvdC40LUg0YfQsNGB0YLQvtGC0YsKYGBge3IsIGZpZy53aWR0aD0gMTAsIGZpZy5oZWlnaHQ9IDV9CnBhcihtZnJvdz1jKDEsMikpCnNwZWMucGdyYW0oY29zKDIqcGkqMTo5Mi8xMCksIGRldHJlbmQgPSBGQUxTRSwgZmFzdCA9IEZBTFNFLCBsb2c9J3llcycsIHRhcGVyPTAsIHBhZD0wKSAj0YDQsNGB0YLQtdC60LDQtdGC0YHRjwpzcGVjLnBncmFtKGNvcygyKnBpKjE6MTAwLzEwKSwgZGV0cmVuZCA9IEZBTFNFLCBmYXN0ID0gRkFMU0UsIGxvZz0neWVzJywgdGFwZXI9MCwgcGFkID0gMCkj0L3QtSDRgNCw0YHRgtC10LrQsNC10YLRgdGPCmBgYAoKIyPQntGC0YHRgtGD0L/Qu9C10L3QuNC1OiDQsdC10LvRi9C5INGI0YPQvAoKKiAkd190LCB0ID0gMCwgXHBtIDEsIFxwbSAyLCBcbGRvdHMkIC0tINC90LXQutC+0YDRgNC10LvQuNGA0L7QstCw0L3Ri9C1INGB0LvRg9GH0LDQudC90YvQtSDQstC10LvQuNGH0LjQvdGLLCAkXG1hdGhiYntFfXdfdCA9IDAsflxzaWdtYV93XjIkLCDQv9C40YjRg9GCLCDQvdCw0L/RgNC40LzQtdGALCAkd190IFxzaW0gd24oMCx+XHNpZ21hX3deMikkLgoKKiDQtdGB0LvQuCDQutGA0L7QvNC1INGC0L7Qs9C+ICR3X3QkIC0tIGlpZCwg0L/QuNGI0YPRgiAkaWlkKDAsflxzaWdtYV93XjIpJAoKKiDQtdGB0LvQuCAkd190JCAtLSBpaWQg0LggJHdfdCBcc2ltIE4oMCwgflxzaWdtYV93XjIpJCwg0YjRg9C8INCz0LDRg9GB0YHQvtCy0YHQutC40LkKCtCf0LXRgNC40L7QtNC+0LPRgNCw0LzQvNCwINCz0LDRg9GB0YHQvtCy0YHQutC+0LPQviDRiNGD0LzQsApgYGB7ciwgZmlnLndpZHRoPSAxMCwgZmlnLmhlaWdodD0gNX0Kd24gPC0gdHMocm5vcm0oMTAwMCkpCnNwZWMucGdyYW0od24sIGRldHJlbmQgPSBGQUxTRSwgbG9nID0gJ25vJykKYGBgCgoKCgpgYGB7ciwgZmlnLndpZHRoPSAxMCwgZmlnLmhlaWdodD0gNX0Kc3BlYy5wZ3JhbSh3biwgZGV0cmVuZCA9IEZBTFNFLCBsb2cgPSAneWVzJykKYGBgCgoKCtCQ0LLRgtC+0LrQvtCy0LDRgNC40LDRhtC40L7QvdC90LDRjyDRhNGD0L3QutGG0LjRjwpgYGB7ciwgZmlnLndpZHRoPSAxMCwgZmlnLmhlaWdodD0gNX0KYWNmKHduKQpgYGAKCgoKI0PQs9C70LDQttC40LLQsNC90LjQtSDQstGA0LXQvNC10L3QvdC+0LPQviDRgNGP0LTQsCDQuCDQstGL0LTQtdC70LXQvdC40LUg0YLRgNC10L3QtNCwCgrQotGA0LXQvdC0IC0tINC/0L7Qu9C40L3QvtC80LjQsNC70YzQvdCw0Y8g0LDQv9C/0YDQvtC60YHQuNC80LDRhtC40Y8g0L3QtdC60L7RgtC+0YDQvtC5INC/0LDRgNCw0LzQtdGC0YDQuNGH0LXRgdC60L7QuSDRhNGD0L3QutGG0LjQuC4KCtCi0YDQtdC90LQgLS0g0LzQtdC00LvQtdC90L3QviDQvNC10L3Rj9GO0YnQsNGP0YHRjyDQutC+0LzQv9C+0L3QtdC90YLQsCDRgNGP0LTQsC4KCtCi0YDQtdC90LQgLS0g0YHQu9GD0YfQsNC50L3Ri9C5INC/0YDQvtGG0LXRgdGBLgoK0KfQtdC8INC+0YLQu9C40YfQsNC10YLRgdGPINGB0LPQu9Cw0LbQuNCy0LDQvdC40LUg0L7RgiDQstGL0LTQtdC70LXQvdC40Y8g0YLRgNC10L3QtNCwPwoKKiDQodCz0LvQsNC20LjQstCw0L3QuNC1INC00LDRgdGCINC80LXQtNC70LXQvdC90L4g0LzQtdC90Y/RjtGJ0YPRjtGB0Y8g0LrQvtC80L/QvtC90LXQvdGC0YMuINCV0YHQu9C4INC/0L7QvdC40LzQsNGC0Ywg0YLRgNC10L3QtCDQutCw0Log0L3QtdC60L7RgtC+0YDRg9GOINC90LXRgdC70YPRh9Cw0LnQvdGD0Y4g0YTRg9C90LrRhtC40Y4sINGC0L4g0LzQvtC20LXQvCDQv9C+0LvRg9GH0LjRgtGMINGC0YDQtdC90LQsINCwINC80L7QttC10Lwg0Lgg0L3QtdGCLgoKCiMj0JvQuNC90LXQudC90YvQuSDRhNC40LvRjNGC0YAsIEZJUiwg0L/RgNC40YfQuNC90L3Ri9C5INGE0LjQu9GM0YLRgCwg0JDQp9ClLCDQpNCn0KUgCiR4X24kIC0tINGA0Y/QtC4KCtCk0LjQu9GM0YLRgNGLCgoqICR5X2ogPSAoXFBoaShYKSlfaiA9IFxzdW1cbGltaXRzX3tpID0gLVxpbmZ0eX1ee1xpbmZ0eX0gaF9pIHhfe2otIGl9JCAtLSDQu9C40L3QtdC50L3Ri9C5INGE0LjQu9GM0YLRgAoKKiAkXHtoX2lcfSQgLS0g0LjQvNC/0YPQu9GM0YHQvdCw0Y8g0YXQsNGA0LDQutGC0LXRgNC40YHRgtC40LrQsCAoaW1wdWxzZSByZXNwb25zZSkKCiogRklSIC0tIGZpbml0ZSBpbXB1bHNlIHJlc3BvbnNlLCDQutC+0L3QtdGH0L3QvtC1INGH0LjRgdC70L4gJGhfaSQg0L3QtdC90YPQu9C10LLRi9C1LCDRgi7QtS4gJChcUGhpKFgpKV9qID0gXHN1bVxsaW1pdHNfe2kgPSAtcl8xfV57cl8yfSBoX2kgeF97ai0gaX0kCgoqINCf0YDQuNGH0LjQvdC90YvQuSDRhNC40LvRjNGC0YAgKGNhc3VhbCBmaWx0ZXIpLCDRgdC80L7RgtGA0LjQvCDRgtC+0LvRjNC60L4g0LIgItC/0YDQvtGI0LvQvtC1IjogJChcUGhpKFgpKV9qID0gXHN1bVxsaW1pdHNfe2kgPSAwfV57cn0gaF9pIHhfe2otIGl9JAoK0JDQp9ClLCDQpNCn0KUKCiogJEhfe1xQaGl9KHopID0gXHN1bVxsaW1pdHNfaSBoX2kgel57LWl9JCAtLSDQv9C10YDQtdC00LDRgtC+0YfQvdCw0Y8g0YTRg9C90LrRhtC40Y8gKHRyYW5zZmVyIGZ1bmN0aW9uKQoKKiAkfEhfe1xQaGl9KGVee2kyXHBpIHd9KXwkIC0tINCQ0LzQv9C70LjRgtGD0LTQvdC+INGH0LDRgdGC0L7RgtC90LDRjyDRhdCw0YDQsNC60YLQtdGA0LjRgdGC0LjQutCwICjQkNCn0KUsIGZyZXF1ZW5jeSByZXNwb25zZSksINC/0L7QutCw0LfRi9Cy0LDQtdGCINC60LDQuiDQvNC10L3Rj9C10YLRgdGPINCw0LzQv9C70LjRgtGD0LTQsCDQsiDQt9Cw0LLQuNGB0LjQvNC+0YHRgtC4INC+0YIg0YfQsNGB0YLQvtGC0YsKCiogJFx2YXJwaGlfXFBoaSh3KSA9IEFyZyBIX3tcUGhpfShcdmFycGhpXntpMlxwaSB3fSkkIC0tINCk0LDQt9C+0YfQsNGB0YLQvtGC0L3QsNGPINGF0LDRgNCw0LrRgtC10YDQuNGB0YLQuNC60LAgKHBoYXNlIHJlc3BvbnNlKQoKIyND0LrQvtC70YzQt9GP0YnQtdC1INGB0YDQtdC00L3QtdC1CgrQodC60L7Qu9GM0LfRj9GJ0LXQtSDRgdGA0LXQtNC90LXQtSAtLSDQu9C40L3QtdC50L3Ri9C5INGE0LjQu9GM0YLRgCwg0LTQu9GPINC60L7RgtC+0YDQvtCz0L4gJGhfaSA9IFxmcmFjezF9ezJNICsgMX0kLgoKCmBgYHtyfQpNb3ZBdmcgPC0gZnVuY3Rpb24oc2VyaWVzLCBNKXsKICAKICBhdmcgPC0gc2FwcGx5KDE6bGVuZ3RoKHNlcmllcyksIEZVTiA9IGZ1bmN0aW9uKGkpewogICAgbCA8LSAoKGktTSk6KGktMSkpCiAgICBsIDwtIGxbbCA+IDBdCiAgICAKICAgIHIgPC0gKChpICsgMSk6KGkrTSkpCiAgICByIDwtIHJbciA8PSBsZW5ndGgoc2VyaWVzKV0KICAgIAogICAgaCA8LSAxLyhsZW5ndGgocikgKyBsZW5ndGgobCkgKyAxKQogICAgCiAgICBzdW0oc2VyaWVzW2MobCxpLHIpXSAqIGgpIAogIH0pCn0KCgpmcmVxdWVuY3lSZXNwb25zZSA8LSBmdW5jdGlvbihoLCB3KXsKICBmciA8LSB3CiAgaWYobWF4KGZyKSA+IDAuNSkgZnIgPC0gZnIgLyAobWF4KHcpICogMikKICBzYXBwbHkoZnIsIGZ1bmN0aW9uKGZyZXEpewogICAgY3MgPC0gY29zKDIqcGkqZnJlcSAqKDE6bGVuZ3RoKGgpKSkKICAgIHByIDwtIGggKiBjcwogICAgcmV0dXJuKGFicyhzdW0ocHIpKSkKICB9KQp9CgpwZXJpb2RvZ3JhbUJ1aWxkZXIgPC0gZnVuY3Rpb24oc2VyaWVzLCBmaWx0ZXJlZCl7CiAgIHBhcihtZnJvdz1jKDEsMykpCiAgIAogICBzcC5pbml0aWFsIDwtIHNwZWMucGdyYW0oc2VyaWVzLCBkZXRyZW5kID0gRkFMU0UsIGZhc3QgPSBGQUxTRSwgbG9nPSdubycsIHRhcGVyPTApCiAgIHNwLmZpbHRlcmVkIDwtIHNwZWMucGdyYW0obmEub21pdChmaWx0ZXJlZCksIGRldHJlbmQgPSBGQUxTRSwgZmFzdCA9IEZBTFNFLCBsb2c9J25vJywgdGFwZXI9MCkKICAgc3AuZGlmZmVyZW5jZSA8LSBzcGVjLnBncmFtKG5hLm9taXQoc2VyaWVzIC0gZmlsdGVyZWQpLCBkZXRyZW5kID0gRkFMU0UsIGZhc3QgPSBGQUxTRSwgbG9nPSdubycsIHRhcGVyPTApCiAgIHBhcihtZnJvdz1jKDEsMSkpCn0KCgptb3ZpbmdBdmVyYWdlRmlsdGVyaW5nIDwtIGZ1bmN0aW9uKHRpbWVTZXJpZXMsbSl7CiAgaWYoICFpcy50cyh0aW1lU2VyaWVzKSl7CiAgICBzdG9wKCJPYmplY3QgZ2l2ZW4gaXMgbm90IGEgdHMgb2JqZWN0ISIpCiAgfQogIAogIHBhcihtZnJvdz1jKDEsMSkpCiAgI0luaXRpYWwKICBwbG90KHRpbWVTZXJpZXMsIHR5cGU9J2wnLCBsdHk9MiwgeWxhYiA9ICdSYXRlJykKICAKICBoIDwtIHJlcCgxLCgyKm0rMSkpLygyKm0rMSkKICAjYXZlcmFnZQogIGZpbHRlcmVkIDwtIHRzKE1vdkF2Zyh0aW1lU2VyaWVzLCBtKSwgc3RhcnQgPSBjKDE5ODAsIDEpLCBmcmVxdWVuY3kgPSAxMikgCiAgbGluZXMoZmlsdGVyZWQsIGNvbD0ncmVkJykKICAjZGlmZmVyZW5jZSAoZGV0cmVuZGVkKQogIGRpZmZlcmVuY2UgPC0gdGltZVNlcmllcyAtIGZpbHRlcmVkCiAgcGxvdChkaWZmZXJlbmNlLCB0eXBlID0gJ2wnKQogIAogICNjb21wYXJpbmcgcGVyaW9kb2dyYW1zCiAgcGVyaW9kb2dyYW1CdWlsZGVyKHRpbWVTZXJpZXMsIGZpbHRlcmVkKQoKICAjRnJlcXVlbmN5IHJlc3BvbnNlCiAgcGFyKG1mcm93PWMoMSwxKSkKICBzcC5maWx0ZXJlZCA8LSBzcGVjLnBncmFtKG5hLm9taXQoZmlsdGVyZWQpLCBkZXRyZW5kID0gRkFMU0UsIGZhc3QgPSBGQUxTRSwgbG9nPSdubycsIHRhcGVyPTAsIHBsb3QgPSBGQUxTRSkKICBmci5maWx0ZXJlZCA8LSBmcmVxdWVuY3lSZXNwb25zZShoLCBzcC5maWx0ZXJlZCRmcmVxKQogIHBsb3QoeCA9IHNwLmZpbHRlcmVkJGZyZXEsIHkgPSBmci5maWx0ZXJlZCwgdHlwZSA9ICdsJywgeGxhYiA9ICJmcmVxdWVuY3kiLCB5bGFiID0gInJlc3BvbnNlIikKfQpgYGAKCtCh0LPQu9Cw0LTQuNC8INGA0Y/QtC4g0JLRi9Cx0LXRgNC10Lwg0L7QutC90L4g0LrRgNCw0YLQvdC+0LUgMTIsINGH0YLQvtCx0Ysg0YPQsdGA0LDRgtGMINGB0LXQt9C+0L3QvdC+0YHRgtGMLgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTV9Cm1vdmluZ0F2ZXJhZ2VGaWx0ZXJpbmcodWssIDEyKQpgYGAK0JLRgdGPINC/0LXRgNC40L7QtNC40LrQsCDRg9GI0LvQsCDQsiDQvtGB0YLQsNGC0L7Qui4g0JrQvtC80L/QvtC90LXQvdGC0LAsINGB0L7QvtGC0LLQtdGC0YHRgtCy0YPRjtGJ0LDRjyDQvdC40LfQutC+0Lkg0YfQsNGB0YLQvtGC0LUsINGB0LzQtdGI0LDQu9Cw0YHRjCDRgSDRgtGA0LXQvdC00L7QvC4KCtCj0LLQuNC70LjRh9C40LLQsNC10Lwg0L7QutC90L4KYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD01fQptb3ZpbmdBdmVyYWdlRmlsdGVyaW5nKHVrLCAyNCkKbW92aW5nQXZlcmFnZUZpbHRlcmluZyh1aywgMzYpCm1vdmluZ0F2ZXJhZ2VGaWx0ZXJpbmcodWssIDYwKQpgYGAK0KfQtdC8INCx0L7Qu9GM0YjQtSDQvtC60L3Qviwg0YLQtdC8INGB0LjQu9GM0L3QtdC1INC/0L7QtNCw0LLQu9GP0LXQvCDQstGL0YHQvtC60LjQtSDRh9Cw0YHRgtC+0YLRiywg0YfRgtC+INCy0LjQtNC90L4g0L/QviDQkNCn0KUuCgoiTG93LXBhc3MgZmlsdGVyIiAtLSDQstGL0YHQvtC60LjQtSDRh9Cw0YHRgtC+0YLRiyDRg9Cx0LjRgNCw0LXQvCwg0L3QuNC30LrQuNC1INC+0YHRgtCw0LLQu9GP0LXQvC4KCiMj0KHQvNC10YnQtdC90LjQtSDQv9GA0Lgg0YHQs9C70LDQttC40LLQsNC90LjQuCDRhNC40LvRjNGC0YDQvtC8INGB0LrQvtC70YzQt9GP0YnQtdCz0L4g0YHRgNC10LTQvdC10LPQvi4g0KDQvtC70Ywg0LLRgtC+0YDQvtC5INC/0YDQvtC40LfQstC+0LTQvdC+0LkKCtCe0LHRidC40Lkg0LLQuNC0INGE0LjQu9GM0YLRgNCwOgpcYmVnaW57YWxpZ24qfQogIHkoYSkgPSBcaW50X3stXGRlbHRhfV57XGRlbHRhfSBmKGEgKyB4KSB3KHgpIGR4LCBccXVhZCBcaW50X3stXGRlbHRhfV57XGRlbHRhfSB3KHgpZHggPSAxLFxxdWFkIFxpbnRfey1cZGVsdGF9XntcZGVsdGF9IHggdyh4KWR4ID0gMC4gClxlbmR7YWxpZ24qfQoK0J/Rg9GB0YLRjCAkZiQgLS0g0L3QtdC60L7RgtC+0YDQsNGPINCz0LvQsNC00LrQsNGPINGE0YPQvdC60YbQuNGPLCDQv9GA0LjQvNC10L3QuNC8INC6INC90LXQuSDRhNC40LvRjNGC0YAuCgpcYmVnaW57YWxpZ24qfQogIGYoYSArIHgpID0gZihhKSArIGYnKGEpeCArIGYnJyhhKVxmcmFje3heMn17Mn0gKyBcbGRvdHMsXFwKICB5KGEpIFxhcHByb3ggZihhKSArIGYnKGEpIDAgKyBmJycoYSlcZnJhY3t4XjJ9ezJ9IFxpbnRfey1cZGVsdGF9XntcZGVsdGF9IHheMiB3KHgpZHguClxlbmR7YWxpZ24qfQoK0JIg0YHQu9GD0YfQsNC1INGB0LrQvtC70YzQt9GP0YnQtdCz0L4g0YHRgNC10LTQvdC10LPQviAkdyh4KSA9IFxmcmFjezF9ezJcZGVsdGF9JCwg0YHQu9C10LTQvtCy0LDRgtC10LvRjNC90L4sINGDINC90LDRgSDQstGB0LXQs9C00LAg0LHRg9C00LXRgiDRgdC80LXRidC10L3QuNC1ICRcYXBwcm94IFxmcmFje1xkZWx0YV4yfXszfSBcZnJhY3tmJycoYSl9ezJ9JC4gCgojI9Ce0YLRgdGC0YPQv9C70LXQvdC40LU6INC/0LXRgNC10YDQuNGB0L7QstC60LAg0Lgg0LfQsNC/0LDQt9C00YvQstCw0L3QuNC1CtCf0LXRgNC10YDQuNGB0L7QstC60LAKCtCS0L7Qt9C90LjQutCw0LXRgiDQuNC3LdC30LAg0YLQvtCz0L4sINGH0YLQviDQvdCw0Lwg0L3QtdGF0LLQsNGC0LDQtdGCINGC0L7Rh9C10Log0YHQv9GA0LDQstCwLCDRh9GC0L7QsdGLINC/0L7RgdGH0LjRgtCw0YLRjCDRgdGA0LXQtNC90LXQtS4g0J/RgNC4INC00L7QsdCw0LLQu9C10L3QuNC4INC90L7QstC+0Lkg0YLQvtGH0LrQuCDRgdGA0LXQtNC90LXQtSDQv9C10YDQtdGB0YfQuNGC0YvQstCw0LXRgtGB0Y8uCgoK0J/QvtGB0LvQtdC00L7QstCw0YLQtdC70YzQvdC+INC00L7QsdCw0LLQu9GP0LXQvCDQv9C+0YHQu9C10LTQvdC40LUgMTAwINGC0L7Rh9C10LouCmBgYHtyfQpwbG90KHkgPSB1aywgeCA9ICgxOjQ0NCksIHR5cGUgPSAnbCcpCmZvcihpIGluIDEwMDoxKXsKICBtYSA8LSBNb3ZBdmcoaGVhZCh1aywgLWkpLCAxMikKICBsaW5lcyhtYSwgdHlwZSA9ICdsJywgY29sID0gJ3JlZCcsIHlsaW0gPSBjKDAsIDYwMDApLCB4bGltID0gYygwLCA0NTApKQp9CmBgYAoK0JfQsNC/0LDQt9C00YvQstCw0L3QuNC1CgrQoNCw0YHRgdC80L7RgtGA0LjQvCDQv9GA0LjRh9C40L3QvdGL0Lkg0YTQuNC70YzRgtGALCDQvdCw0L/RgNC40LzQtdGAINGB0LrQvtC70YzQt9GP0YnQtdC1INGB0YDQtdC00L3QtdC1LCDQutC+0YLQvtGA0L7QtSDRgdGH0LjRgtCw0LXRgtGB0Y8g0YLQvtC70YzQutC+INC/0L4gItC/0YDQvtGI0LvQvtC80YMiLgoKYGBge3J9Ck1vdkF2Zy5jYXVzZSA8LSBmdW5jdGlvbihzZXJpZXMsIE0pewogIGF2ZyA8LSBzYXBwbHkoMTpsZW5ndGgoc2VyaWVzKSwgRlVOID0gZnVuY3Rpb24oaSl7CiAgICBsIDwtICgoaS0gTSArIDEpOihpLTEpKQogICAgbCA8LSBsW2wgPiAwXQogICAgaCA8LSAxL2xlbmd0aChsKQogICAgc3VtKHNlcmllc1tjKGwsaSldICogaCkgCiAgfSkKfQpgYGAKCmBgYHtyfQpwbG90KHkgPSB1aywgeCA9ICgxOjQ0NCksIHR5cGUgPSAnbCcpCm1hLmMgPC0gTW92QXZnLmNhdXNlKHVrLCAyNCkKbWEgPC0gTW92QXZnKHVrLCAyNCkKbGluZXMobWEuYywgdHlwZSA9ICdsJywgY29sID0gJ3JlZCcsIHlsaW0gPSBjKDAsIDYwMDApLCB4bGltID0gYygwLCA0NTApKQpsaW5lcyhtYSwgdHlwZSA9ICdsJywgY29sID0gJ2JsdWUnLCB5bGltID0gYygwLCA2MDAwKSwgeGxpbSA9IGMoMCwgNDUwKSkKbGVnZW5kKCJ0b3BsZWZ0IiwgYygiVXN1YWwiLCAiQ2FzdWFsIiksIGx0eT1jKDEsMSksIGx3ZD1jKDIuNSwyLjUpLGNvbD1jKCJibHVlIiwicmVkIikpCmBgYArQktC40LTQvdC+LCDRh9GC0L4gY2FzdWFsINGE0LjQu9GM0YLRgCAi0LTQvtCz0L7QvdGP0LXRgiIg0L7QsdGL0YfQvdC+0LUg0YHRgNC10LTQvdC10LUg0YLQvtC70YzQutC+INGH0LXRgNC10LcgINC90LXQutC+0YLQvtGA0L7QtSDQutC+0LvQuNGH0LXRgdGC0LLQviDRgtC+0YfQtdC6LgoKCgojI9Ch0LrQvtC70YzQt9GP0YnQsNGPINC80LXQtNC40LDQvdCwCgrQkiDQvtGC0LvQuNGH0LjQtSDQvtGCINGB0LrQvtC70YzQt9GP0LXRidC10LPQviDRgdGA0LXQtNC90LXQs9C+LCDRg9GB0YLQvtC50YfQuNCy0LAg0Log0LDRg9GC0LvQsNC10YDQsNC8LiDQndC+INC90LXQs9C70LDQtNC60LDRjyDQuCDQvdC1INGP0LLQu9GP0LXRgtGB0Y8g0L7RhtC10L3QutC+0Lkg0Lwu0L4uINC00LvRjyDQvdC10YHQuNC80LzQtdGC0YDQuNGH0L3QvtCz0L4g0YDRj9C00LAuINCY0L3QvtCz0LTQsCDRgdC90LDRh9Cw0LvQsCDQv9GA0LjQvNC10L3Rj9GO0YIg0YHQutC+0LvRjNC30Y/RidGD0Y4g0LzQtdC00LjQsNC90YMsINCwINC/0L7RgtC+0Lwg0YDQtdC30YPQu9GM0YLQsNGCINGB0LPQu9Cw0LbQuNCy0LDRjtGCINGB0LrQvtC70YzQt9GP0YnQuNC8INGB0YDQtdC00L3QuNC8LiAKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NX0KbW92aW5nTWVkaWFuRmlsdGVyaW5nIDwtIGZ1bmN0aW9uKHRpbWVTZXJpZXMsayl7CiAgaWYoICFpcy50cyh0aW1lU2VyaWVzKSl7CiAgICBzdG9wKCJPYmplY3QgZ2l2ZW4gaXMgbm90IGEgdHMgb2JqZWN0ISIpCiAgfQogIAogIHBhcihtZnJvdz1jKDEsMSkpCiAgI0luaXRpYWwKICBwbG90KHRpbWVTZXJpZXMsIHR5cGU9J2wnLCBsdHk9MiwgeWxhYiA9ICdSYXRlJykKICAjbWVkaWFuCiAgZmlsdGVyZWQgPC0gcnVubWVkKHRpbWVTZXJpZXMsIGspCiAgbGluZXModHMoZmlsdGVyZWQsIHN0YXJ0ID0gYygxOTgwLCAxKSwgZnJlcXVlbmN5ID0gMTIgKSAsIGNvbD0ncmVkJywgdHlwZT0nbCcpCiAgI2RpZmZlcmVuY2UgKGRldHJlbmRlZCkKICBkaWZmZXJlbmNlIDwtIHRpbWVTZXJpZXMgLSBmaWx0ZXJlZAogIHBsb3QoZGlmZmVyZW5jZSwgdHlwZSA9ICdsJykKICAKICAjY29tcGFyaW5nIHBlcmlvZG9ncmFtcwogIAogIHBlcmlvZG9ncmFtQnVpbGRlcih0aW1lU2VyaWVzLCBmaWx0ZXJlZCkKfQoKCm1vdmluZ01lZGlhbkZpbHRlcmluZyh1aywgMTMpCmBgYAoKIyPQoNCw0LfQvdC+0YHRgtGMINC/0LXRgNCy0L7Qs9C+INC/0L7RgNGP0LTQutCwCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NX0KZGYgPC0gZGlmZih1aykKaC5kZiA8LSBjKC0xLDEpCnBsb3QoZGYsIHR5cGU9J2wnLCBtYWluPSdkaWZmZXJlbmNlJykKcGFyKG1mcm93PWMoMSwyKSkKc3AuaW5pdGlhbCA8LSBzcGVjLnBncmFtKHVrLCBkZXRyZW5kID0gRkFMU0UsIGZhc3QgPSBGQUxTRSwgbG9nPSdubycsIHRhcGVyPTApCnNwLmRmIDwtIHNwZWMucGdyYW0obmEub21pdChkZiksIGRldHJlbmQgPSBGQUxTRSwgZmFzdCA9IEZBTFNFLCBsb2c9J25vJywgdGFwZXI9MCkKcGFyKG1mcm93PWMoMSwxKSkKZnIuZGYgPC0gZnJlcXVlbmN5UmVzcG9uc2UoaC5kZiwgc3AuZGYkZnJlcS8xMikKcGxvdCh4ID0gc3AuZGYkZnJlcSwgeSA9IGZyLmRmLCB0eXBlID0gJ2wnLCB4bGFiID0gJ2ZyZXF1ZW5jeScsIHlsYWIgPSAncmVzcG9uc2UnKQoKYGBgCiJIaWdoLXBhc3MgZmlsdGVyIiAtLSDQv9GA0L7Qv9GD0YHQutCw0LXRgiDQstGL0YHQvtC60LjQtSDRh9Cw0YHRgtC+0YLRiywg0L3QuNC30LrQuNC1INGD0LHQuNGA0LDQtdGCLgoK0J/QvtGH0LXQvNGDINC/0LXRgNC10YXQvtC0INC6INGA0LDQt9C90L7RgdGC0Y/QvCDRjdGC0L4g0YXQvtGA0L7RiNC+PwoKKiDQldGB0LvQuCDQvNC+0LTQtdC70Ywg0L3QsNGI0LXQs9C+INGA0Y/QtNCwINC40LzQtdC10YIg0LLQuNC0ICR4X3QgPSBcbXVfdCArIHlfdCQsINCz0LTQtSAkXG11X3QkIC0tINGC0YDQtdC90LQsICR5X3QkIC0tINGB0YLQsNGG0LjQvtC90LDRgNC90YvQuSDQv9GA0L7RhtC10YHRgSwg0YLQviDQv9GA0Lgg0L/QtdGA0LXRhdC+0LTQtSDQuiDRgNCw0LfQvdC+0YHRgtGP0Lwg0L/QvtC70YPRh9C40Lwg0YHRgtCw0YbQuNC+0L3QsNGA0L3Ri9C5INGA0Y/QtCwg0L7RgdC+0LHQtdC90L3Qviwg0LXRgdC70Lgg0YLRgNC10L3QtCDRhNC40LrRgdC40YDQvtCy0LDQvSAo0L3QsNC/0YDQuNC80LXRgCwg0LvQuNC90LXQudC90YvQuSkKCiog0KPQsdC10YDQtdC8INC90LjQt9C60LjQtSDRh9Cw0YHRgtC+0YLRiywg0YPRgdC40LvQuNC8INCy0LrQu9Cw0LQg0LLRi9GB0L7QutC40YUKCiog0KPQsdC10YDQtdC8INC70LjQvdC10LnQvdGL0Lkg0YLRgNC10L3QtCAo0LXRgdC70Lgg0L/QtdGA0LXQudC00LXQvCDQuiDRgNCw0LfQvdC+0YHRgtGP0Lwg0LLRgtC+0YDQvtCz0L4g0L/QvtGA0Y/QtNC60LAsINGC0L4g0LrQstCw0LTRgNCw0YLQuNGH0L3Ri9C5INC4INGCLtC0LikKCtCc0LjQvdGD0YHRiwoKKiDQo9Cy0LXQu9C40YfQuNCy0LDQtdC8INCy0LrQu9Cw0LQg0YjRg9C80LAKCgoKI9Ch0LPQu9Cw0LbQuNCy0LDQvdC40LUg0Lgg0LLRi9C00LXQu9C10L3QuNC1INGC0YDQtdC90LTQsCDRgSDQv9C+0LzQvtGJ0YzRjiDRgNC10LPRgNC10YHRgdC40LgKIyPQm9C40L3QtdC50L3QsNGPINGA0LXQs9GA0LXRgdGB0LjRjwoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD01fQpmaXQgPC0gbG0oZGYudWskRVhQIH4gZGYudWskREFURSwgdW5lbXBsb3ltZW50LmRhdGEpCnBsb3QoZGYudWssIHR5cGU9J2wnKQphYmxpbmUoZml0LCBjb2w9J3JlZCcpCnBlcmlvZG9ncmFtQnVpbGRlcihkZi51ayRFWFAsIGZpdCRmaXR0ZWQudmFsdWVzKQpwbG90KGZpdCRyZXNpZHVhbHMsIHR5cGU9J2wnKQpgYGAKCiMj0J/QvtC70LjQvdC+0LzQuNCw0LvRjNC90LDRjyDRgNC10LPRgNC10YHRgdC40Y8KYGBge3J9CmNzIDwtIGNvcygyKnBpKigxOmxlbmd0aCh1aykpLzEyKQpzbiA8LSBzaW4oMipwaSooMTpsZW5ndGgodWspKS8xMikKCmZpdCA8LSBsbShFWFAgfiBzdGF0czo6cG9seShkZi51ayREQVRFLCAxMCkKICAgICAgICAgICAgICAgICAgLCBkZi51aykKcGxvdChkZi51aywgdHlwZT0nbCcsIHlsYWI9IkVYUCIpCmxpbmVzKHk9IGZpdHRlZChmaXQpLCB4PSBkZi51ayREQVRFLCBjb2w9J3JlZCcpCnBsb3QoZml0JHJlc2lkdWFscywgdHlwZT0nbCcpCnBlcmlvZG9ncmFtQnVpbGRlcihkZi51ayRFWFAsIGZpdCRmaXR0ZWQudmFsdWVzKQpgYGAKCiMjS2VybmVsIFNtb290aGluZyAoa2VybmVsIHJlZ3Jlc3Npb24pCgpLZXJuZWwgU21vb3RoaW5nIC0tINGB0LrQvtC70YzQt9GP0YnQtdC1INGB0YDQtdC00L3QtdC1LCDQsiDQutC+0YLQvtGA0L7QvCDQuNGB0L/QvtC70YzQt9GD0LXRgtGB0Y8g0LLQtdGB0L7QstCw0Y8g0YTRg9C90LrRhtC40Y8gKGtlcm5lbCkuCgpD0YfQuNGC0LDQtdC8LCDRh9GC0L4g0L3QsNGIINGA0Y/QtCDQuNC80LXQtdGCINCy0LjQtDoKXGJlZ2lue2FsaWduKn0KICB4X3QgPSBmX3QgKyB5X3QsClxlbmR7YWxpZ24qfQrQs9C00LUgJGZfdCQgLS0g0L3QtdC60L7RgtC+0YDQsNGPINCz0LvQsNC00LrQsNGPINGE0YPQvdC60YbQuNGPLCAkeV90JCAtLSDRgdGC0LDRhtC40L7QvdCw0YDQvdGL0Lkg0L/RgNC+0YbQtdGB0YEuINCi0L7Qs9C00LAgClxiZWdpbnthbGlnbip9ClxoYXR7Zn1fdCA9IFxzdW1fe2kgPSAxfV57bn13X2kodCl4X2ksClxlbmR7YWxpZ24qfQrQs9C00LUKXGJlZ2lue2FsaWduKn0Kd19pKHQpID0gSyhcZnJhY3t0LWl9e2J9KSAvIFxzdW1fe2ogPSAxfV57bn1LKFxmcmFje3Qtan17Yn0pLgpcZW5ke2FsaWduKn0KCtCe0LHRi9GH0L3QviAkSyh6KSA9IFxmcmFjezF9e1xzcXJ0ezJccGl9fSBcZXhwKC16XjIgLyAyKSQuCgpgYGB7cn0KcGxvdCh1aywgdHlwZT0nbCcsIHlsYWI9IkVYUCIpCnRyZW5kLnNtIDwtIGtzbW9vdGgodGltZSh1ayksIHVrLCAnbm9ybWFsJywgYmFuZHdpZHRoID0gMikKbGluZXModHJlbmQuc20sIGNvbD0ncmVkJykKCnBsb3QodWsgLSB0cmVuZC5zbSR5KQpwZXJpb2RvZ3JhbUJ1aWxkZXIodWssIHRyZW5kLnNtJHkpCmBgYAoKIyNOZWFyZXN0IE5laWdoYm9yIFJlZ3Jlc3Npb24KCtCh0YLRgNC+0LjQvCDQu9C40L3QtdC50L3Rg9GOINGA0LXQs9GA0LXRgdGB0LjRjiDQvdCwICRrJCDQsdC70LjQttCw0LnRiNC40YUg0YHQvtGB0LXQtNC10LkuINCiLtC1LiDQv9GA0LXQtNGB0LrQsNC30YvQstCw0LXQvCAkeF90JCDQv9C+ICRce3hfe3Qtay8yfSxcbGRvdHMsIHhfdCxcbGRvdHMsIHhfe3Qray8yfVx9JC4KCmBgYHtyfQpwbG90KHVrLCB0eXBlPSdsJywgeWxhYj0ibSDCoyIpCm5laWdoLnQgPC0gc3Vwc211KHRpbWUodWspLCB1aywgc3BhbiA9IC41KQpuZWlnaC5zIDwtIHN1cHNtdSh0aW1lKHVrKSwgdWssIHNwYW4gPSAuMDEpCmxpbmVzKG5laWdoLnQsIGNvbD0ncmVkJykKbGluZXMobmVpZ2gucywgY29sPSdibHVlJykKCgpwbG90KHVrIC0gbmVpZ2gudCR5KQpwZXJpb2RvZ3JhbUJ1aWxkZXIodWssIG5laWdoLnQkeSkKcGxvdCh1ayAtIG5laWdoLnMkeSkKcGVyaW9kb2dyYW1CdWlsZGVyKHVrLCBuZWlnaC5zJHkpCmBgYAoKI1NlYXNvbmFsIGFuZCBUcmVuZCBEZWNvbXBvc2l0aW9uIChTVEwpCgoK0KXQvtGC0LjQvCDQv9GA0LXQtNGB0YLQsNCy0LjRgtGMINGA0Y/QtCDQsiDQstC40LTQtSAkWF9uID0gVF9uICsgU19uICsgTl9uJC4KCtCR0YPQtNC10Lwg0LTQtdC70LDRgtGMINCy0YHQtSDQtNC70Y8g0LvQvtCz0LDRgNC40YTQvNC40YDQvtCy0LDQvdC90L7Qs9C+INGA0Y/QtNCwLCDRgtCw0Log0LrQsNC6INC+0L0g0L/QvtGF0L7QtiDQvdCwINC70LjQvdC10LnQvdGD0Y4g0LzQvtC00LXQu9GMICjQsNC80L/Qu9C40YLRg9C00LAg0L/RgNC40LzQtdGA0L3QviDQvtC00LjQvdCw0LrQvtCy0LApLiDQnNC+0LbQvdC+INC30LDQvNC10L3QuNGC0Ywg0LTQsNC70LXQtSDRgdC70L7QttC10L3QuNC1INC90LAg0YPQvNC90L7QttC10L3QuNC1LCDRgNCw0LfQvdC+0YHRgtGMINC90LAg0LTQtdC70LXQvdC40LUg0Lgg0L/QvtC70YPRh9C40YLRjCDQsNC70LPQvtGA0LjRgtC8INC00LvRjyDQvNGD0LvRjNGC0LjQv9C70LjQutCw0YLQuNCy0L3QvtC5INC80L7QtNC10LvQuC4KCtCl0L7RgNC+0YjQtdC1INC+0L/QuNGB0LDQvdC40LUg0LDQu9Cz0L7RgNC40YLQvNCwINC90LDRiNC10Lsg0LfQtNC10YHRjDoKaHR0cDovL3d3dy5hYnMuZ292LmF1L3dlYnNpdGVkYnMvZDMzMTAxMTQubnNmLzUxYzlhM2QzNmVkZmQwZGZjYTI1NmFjYjAwMTE4NDA0L2M4OTBhYThlNjU5NTczOTdjYTI1NmNlMTAwMThjOWQ4IW9wZW5kb2N1bWVudAoKKiDQndCw0YfQsNC70YzQvdCw0Y8g0L7RhtC10L3QutCwINGC0YDQtdC90LTQsC4g0J/RgNC40LzQtdC90Y/QtdC8INGB0LrQvtC70YzQt9GP0YnQtdC1INGB0YDQtdC00L3QtdC1INGBINC00LvQuNC90L7QuSDQvtC60L3QsCBULCDQsiDQvdCw0YjQtdC8INGB0LvRg9GH0LUg0LrRgNCw0YLQvdC+0LkgMTIuCgoKYGBge3J9ClQxIDwtIE1vdkF2Zyhsb2codWspLCAyNCkKU04xIDwtIGxvZyh1aykgLSBUMQpwZXJpb2RvZ3JhbUJ1aWxkZXIobG9nKHVrKSwgVDEpCmBgYAoKKiDQn9C+0LvRg9GH0LjQu9C4INGA0Y/QtCDQsiDQstC40LTQtSAkWF9uID0gXHdpZGV0aWxkZXtUfV9uICsgXHdpZGV0aWxkZXtTX24gKyBOX259JAoKKiAg0J7RhtC10L3QutCwINGB0LXQt9C+0L3QvdC+0Lkg0LrQvtC80L/QvtC90LXQvdGC0YsuINCh0LPQu9Cw0LbQuNCy0LDQtdC8ICRcd2lkZXRpbGRle1NfbiArIE5fbn0kINGBINC80LDQu9C10L3RjNC60LjQvCDQvtC60L3QvtC8LiDQn9C+0LvRg9GH0LDQtdC8ICRcd2lkZXRpbGRle1NfbiArIE5fbn0gPSBcd2lkZXRpbGRle1N9X24gKyBcd2lkZXRpbGRle059X24kLiAKCiog0KPQsdC40YDQsNC10Lwg0L/QvtC70YPRh9C10L3QvdGD0Y4g0YHQtdC30L7QvdC90L7RgdGC0Ywg0LjQtyDQuNGB0YXQvtC00L3QvtCz0L4g0YDRj9C00LAsINC/0L7Qu9GD0YfQsNC10Lwg0L7RhtC10L3QutGDINC40YHQv9GA0LDQstC70LXQvdC90L7Qs9C+INGA0Y/QtNCwIChhZGp1c3RlZCkuCgoKYGBge3J9ClMxIDwtIE1vdkF2ZyhTTjEsIDIpCkFESjEgPC0gbG9nKHVrKSAtIFMxCnBlcmlvZG9ncmFtQnVpbGRlcihTTjEsIFMxKQpgYGAKKiDQo9C70YPRh9GI0LXQvdC90LDRjyDQvtGG0LXQvdC60LAg0YLRgNC10L3QtNCwLiDQn9GA0LjQvNC10L3Rj9C10Lwg0YHQutC+0LvRjNC30Y/RidC10LUg0YHRgNC10LTQvdC10LUg0YEg0LHQvtC70YzRiNC40Lwg0L7QutC90L7QvCDQuiDQuNGB0L/RgNCw0LLQu9C10L3QvdC+0LzRgyDRgNGP0LTRgyAkWF9uIC0gXHdpZGV0aWxkZXtTfV9uJCwg0L/QvtC70YPRh9Cw0LXQvCAkXHdpZGV0aWxkZXtcd2lkZXRpbGRle1R9fV9uICsgIFx3aWRldGlsZGV7XHdpZGV0aWxkZXtOfX1fbiQKCiog0J7RgtGB0Y7QtNCwINCy0YLQvtGA0L7QuSDRgNCw0Lcg0L7RhtC10L3QuNCy0LDQtdC8ICRcd2lkZXRpbGRle1x3aWRldGlsZGV7U19uICsgTl9ufX0gPSBYX24gLSBcd2lkZXRpbGRle1x3aWRldGlsZGV7VH19X24kCgpgYGB7cn0KVDIgPC0gTW92QXZnKEFESjEsIDI0KQpTTjIgPC0gbG9nKHVrKSAtIFQyCnBlcmlvZG9ncmFtQnVpbGRlcihBREoxLCBUMikKYGBgCgoqINCf0YDQuNC80LXQvdGP0LXQvCDRgdC60L7Qu9GM0LfRj9GJ0LXQtSDRgdGA0LXQtNC90LXQtSDRgSDQvNCw0LvQtdC90YzQutC40Lwg0L7QutC90L7QvCDQuiAkXHdpZGV0aWxkZXtcd2lkZXRpbGRle1NfbiArIE5fbn19JCwg0L/QvtC70YPRh9Cw0LXQvCAkXHdpZGV0aWxkZXtcd2lkZXRpbGRle1N9fV9uICsgIFx3aWRldGlsZGV7XHdpZGV0aWxkZXtcd2lkZXRpbGRle059fX1fbiQuIEPQvdC+0LLQsCDQvtGG0LXQvdC40LLQsNC10Lwg0LjRgdC/0YDQsNCy0LvQtdC90L3Ri9C5INGA0Y/QtApgYGB7cn0KUzIgPC0gTW92QXZnKFNOMiwgMikKQURKMiA8LSBsb2codWspIC0gUzIKcGVyaW9kb2dyYW1CdWlsZGVyKFNOMiwgUzIpCmBgYAoKKiDQpNC40L3QsNC70YzQvdCw0Y8g0L7RhtC10L3QutCwINGC0YDQtdC90LTQsCDQuCDRiNGD0LzQsAoKYGBge3J9ClQzIDwtIE1vdkF2ZyhBREoyLCAyNCkKTjEgPC0gQURKMiAtIFQzCnBlcmlvZG9ncmFtQnVpbGRlcihBREoyLCBUMykKYGBgCgoKCiog0J/QvtC70YPRh9C40LvQuCAkWF9uID0gXHdpZGV0aWxkZXtcd2lkZXRpbGRle1R9fV9uICsgXHdpZGV0aWxkZXtcd2lkZXRpbGRle1N9fV9uICsgIFx3aWRldGlsZGV7XHdpZGV0aWxkZXtcd2lkZXRpbGRle059fX1fbiQKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9OH0KcGFyKG1mcm93PWMoNCwxKSkKcGxvdChsb2codWspLCB0eXBlID0gJ2wnLCB5bGFiPSdPcmlnaW5hbCcpCnBsb3QoVDMsIHR5cGU9J2wnLCB5bGFiPSdUcmVuZCcpCnBsb3QoUzIsIHR5cGU9J2wnLCB5bGFiID0gJ1NlYXNvbmFsIENvbXBvbmVudCcpCnBsb3QoTjEsIHR5cGU9J2wnLCB5bGFiPSdOb2lzZScpCmBgYAoKYGBge3J9CmFjZihOMSwgbGFnLm1heCA9IDEwMCkKYGBgCtCo0YPQvCDQsdC10LvRi9C8INC90LUg0L/QvtC70YPRh9C40LvRgdGPLiDQmtCw0Log0LLQuNC00L3QviDQuNC3INC/0L7RgdC70LXQtNC90LjRhSDQv9C10YDQuNC+0LTQvtCz0YDQsNC80LwsINC80L3QvtCz0L4g0L/QtdGA0LjQvtC00LjRh9C10YHQutC40YUg0LrQvtC80L/QvtC90LXQvdGCINGC0LDQuiDQuCDQvdC1INCy0YvQtNC10LvQuNC70LjRgdGMINCyINGB0LXQt9C+0L3QvdGD0Y4g0LrQvtC80L/QvtC90LXQvdGC0YMuCgoKI1NUTCAoTE9FU1MpCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQp1ay5zdGwgPC0gc3RsKHVrLCBzLndpbmRvdyA9IDEzLCBsLndpbmRvdyA9IDEzLCBvdXRlciA9IDAsIGlubmVyID0gMiwgdC53aW5kb3cgPSAzNSkKcGxvdCh1ay5zdGwpCmBgYAoKIyPQodGC0LDQsdC40LvQuNC30LDRhtC40Y8g0LTQuNGB0L/QtdGA0YHQuNC4CgrQntGB0YLQsNGC0L7QuiDQv9C+0YHQu9C1IHN0bApgYGB7cn0KcmVtIDwtIHVrLnN0bCR0aW1lLnNlcmllc1ssM10KcGxvdChyZW0pCmBgYAoK0J/RgNC+0LvQvtCz0L7RgNC40YTQvNC40YDRg9C10Lwg0LjRgdGF0L7QtNC90YvQuSDRgNGP0LQg0Lgg0YHQvdC+0LLQsCDQv9C+0YHQvNC+0YLRgNC40Lwg0L3QsCDQvtGB0YLQsNGC0L7QuiDQv9C+0YHQu9C1INCy0YvQtNC10LvQtdC90LjRjyDRgtGA0LXQvdC00LAuCmBgYHtyfQp1ay5sb2cgPC0gbG9nKHVrKQp1ay5sb2cuc3RsIDwtIHN0bCh1ay5sb2csIHMud2luZG93ID0gMTMsIGwud2luZG93ID0gMTMsIG91dGVyID0gMCwgaW5uZXIgPSAyLCB0LndpbmRvdyA9IDM1KQpsb2cucmVtIDwtIHVrLmxvZy5zdGwkdGltZS5zZXJpZXNbLDNdCnBsb3QobG9nLnJlbSkKYGBgCgrQntGG0LXQvdC40Lwg0LTQuNGB0L/QtdGA0YHQuNGOCgrQmNC00LXRjyAKKiDQktGL0LTQtdC70LjQsiDRgtGA0LXQvdC0LCDQv9C+0LvRg9GH0LjQu9C4INGI0YPQvCDRgSDQvdC10LrQvtGC0L7RgNC+0Lkg0LTQuNGB0L/QtdGA0YHQuNC10LkgJFx4aV9uID0gXHNpZ21hKG4pXGVwc19uJAoKKiDQktGL0LTQtdC70LjRgtGMINGC0YDQtdC90LQg0YMgJFx4aV9uXjIgPSBcc2lnbWEobileMlxlcHNfbl4yJCAtLSDQstGB0LUg0YDQsNCy0L3Qviwg0YfRgtC+INC90LDQudGC0Lgg0YHRgNC10LTQvdC10LUgJFxtYXRoYmZ7RX1ceGlfbl4yID0gXHNpZ21hKG4pXjIkCgrQktC+0LfQstC10LTQtdC8INC+0YHRgtCw0YLQvtC6INCyINC60LLQsNC00YDQsNGCINC4INCy0YvQtNC10LvQuNC8INGC0YDQtdC90LQg0YHQutC+0LvRjNC30Y/RidC40Lwg0YHRgNC10LTQvdC40LwKYGBge3J9CmRpZi5zcXIgPC0gdHMobG9nLnJlbV4yKQpwbG90KGRpZi5zcXIsIHR5cGUgPSAnbCcpCnNpZ21hLm4uc3FyIDwtIE1vdkF2ZyhkaWYuc3FyLCAyNCkKbGluZXMoc2lnbWEubi5zcXIsIGNvbD0ncmVkJykKbGVnZW5kKCJ0b3BsZWZ0IiwgYygiUmVtXjIiLCAic2lnbWEobileMiIpLCBsdHk9YygxLDEpLCBsd2Q9YygyLjUsMi41KSxjb2w9YygiYmxhY2siLCJyZWQiKSkKYGBgCgrQn9C+0YHRgtGA0L7QuNC8INC+0LPQuNCx0LDRjtGJ0YPRjiAKYGBge3J9Cm5vaXNlIDwtIHRzKGxvZy5yZW0gLyBzcXJ0KHNpZ21hLm4uc3FyKSkKcGxvdChsb2cucmVtLCB0eXBlID0gJ2wnKQpsaW5lcyh0cyhzcXJ0KHNpZ21hLm4uc3FyKSwgc3RhcnQgPSBjKDE5ODAsIDEpLCBmcmVxdWVuY3kgPSAxMiksIGNvbCA9ICdibHVlJykKbGluZXModHMoLXNxcnQoc2lnbWEubi5zcXIpLCBzdGFydCA9IGMoMTk4MCwgMSksIGZyZXF1ZW5jeSA9IDEyKSwgY29sID0gJ2JsdWUnKQpgYGAKCtCe0YbQtdC90LrQsCDRiNGD0LzQsCwg0LXQtSDQv9C10YDQuNC+0LTQvtCz0YDQsNC80LzQsCDQuCDQsNCy0YLQvtC60L7QstCw0YDQuNCw0YbQuNC+0L3QvdCw0Y8g0YTRg9C90LrRhtC40Y8KYGBge3J9CnBsb3Qobm9pc2UpCnNwZWMucGdyYW0obmEub21pdChub2lzZSksIGRldHJlbmQgPSBGQUxTRSwgZmFzdCA9IEZBTFNFLCBsb2c9J25vJywgdGFwZXI9MCkKYWNmKG5vaXNlLCBsYWcubWF4ID0gNTApCmBgYAoKCgoKCgoK